// Code Generation for Clang Level 4 compiler/interpreter
// AST version for stack machine (OOP)
// Includes procedures, functions, parameters, arrays, concurrency.
// Display machine.
// P.D. Terry, Rhodes University, 1996

#include "cgen.h"

extern STKMC  *Machine;
extern CGEN   *CGen;
extern REPORT *Report;

// ++++++++++++++ AST node classes +++++++++++++++++++++++++++++++++++++++++

struct NODE {
  int value;       // value derived from this node
  bool defined;    // true if value is defined (for constant nodes)
  bool refnode;    // true if node corresponds to a reference
  NODE()                         { defined = false; refnode = false; }
  virtual void emit1(void)    = 0;
  virtual void emit2(void)    = 0;
  virtual void link(AST next) = 0;
};

struct BINOPNODE : public NODE {
  CGEN_operators op;
  AST left, right;
  BINOPNODE(CGEN_operators O, AST L, AST R) { op = O; left = L; right = R; }
  virtual void emit1(void);      // load value onto stack
  virtual void emit2(void)       {;}
  virtual void link(AST next)    {;}
};

void BINOPNODE::emit1(void)
// load value onto stack resulting from binary operation
{ bool folded = false;
  if (left && right)
  { // Some optimizations (others are left as an exercise).
    // These need improvement so as to perform range checking
    switch (op)
    { case CGEN_opadd:
        if (right->defined && right->value == 0) // x + 0 = x
          { left->emit1(); folded = true; }
        else if (left->defined && left->value == 0) // 0 + x = x
          { right->emit1(); folded = true; }
        break;

      case CGEN_opsub:
        if (right->defined && right->value == 0) // x - 0 = x
          { left->emit1(); folded = true; }
        else if (left->defined && left->value == 0) // 0 - x = -x
          { right->emit1(); CGen->emit(int(STKMC_neg)); folded = true; }
        break;

      case CGEN_opmul:
        if (right->defined && right->value == 1) // x * 1 = x
          { left->emit1(); folded = true; }
        else if (left->defined && left->value == 1) // 1 * x = x
          { right->emit1(); folded = true; }
        else if (right->defined && right->value == 0) // x * 0 = 0
          { right->emit1(); folded = true; }
        else if (left->defined && left->value == 0) // 0 * x = 0
          { left->emit1(); folded = true; }
        break;

      case CGEN_opdvd:
        if (right->defined && right->value == 1) // x / 1 = x
          { left->emit1(); folded = true; }
        else if (right->defined && right->value == 0) // x / 0 = error
          { Report->error(224); folded = true; }
        break;
      // no folding attempted here for relational operations
    }
  }
  if (!folded)
  { if (left) left->emit1(); if (right) right->emit1();
    CGen->emit(int(STKMC_add) + int(op));   // careful - ordering used
  }
  if (left) delete left; if (right) delete right;
}

struct MONOPNODE : public NODE {
  CGEN_operators op;  // for expansion - only negation used here
  AST operand;
  MONOPNODE(CGEN_operators O, AST E) { op = O; operand = E; }
  virtual void emit1(void);      // load value onto stack
  virtual void emit2(void)       {;}
  virtual void link(AST next)    {;}
};

void MONOPNODE::emit1(void)
// load value onto stack resulting from unary operation
{ if (operand) { operand->emit1(); delete operand; }
  CGen->emit(int(STKMC_neg));
}

struct VARNODE : public NODE {
  bool ref;       // direct or indirectly accessed
  int level;      // static level of declaration
  int offset;     // offset of variable assigned by compiler
  VARNODE() {;}   // default
  VARNODE(bool byref, int L, int O)
    { ref = byref; level = L; offset = O; }
  virtual void emit1(void);      // load variable value onto stack
  virtual void emit2(void);      // load variable address onto stack
  virtual void link(AST next)    {;}
};

void VARNODE::emit1(void)
// load variable value onto stack
{ emit2(); CGen->emit(int(STKMC_val)); }

void VARNODE::emit2(void)
// load variable address onto stack
{ CGen->emit(int(STKMC_adr)); CGen->emit(level); CGen->emit(-offset);
  if (ref) CGen->emit(int(STKMC_val));
}

struct INDEXNODE : public VARNODE {
  AST size;      // for range checking
  AST index;     // subscripting expression
  INDEXNODE(bool byref, int L, int O, AST S, AST I)
    { ref = byref; level = L; offset = O; size = S; index = I; }
  virtual void emit2(void);      // load array element address and check
  virtual void link(AST next)    {;}
};

void INDEXNODE::emit2(void)
// load array element address and check in range
{ CGen->emit(int(STKMC_adr)); CGen->emit(level); CGen->emit(-offset);
  if (ref) CGen->emit(int(STKMC_val));
  if (index) { index->emit1(); delete index; }
  if (size) { size->emit1(); delete size; }
  CGen->emit(int(STKMC_ind));
}

// void INDEXNODE::emit1(void) is inherited from VARNODE

struct REFNODE : public VARNODE {
  AST size;
  REFNODE(bool byref, int L, int O, AST S)
    { ref = byref; level = L; offset = O; size = S; refnode = 1; }
  virtual void emit1(void);       // load array argument address and size
  virtual void emit2(void)        {;}
  virtual void link(AST next)     {;}
};

void REFNODE::emit1(void)
// load array argument address and size
{ CGen->emit(int(STKMC_adr)); CGen->emit(level); CGen->emit(-offset);
  if (ref) CGen->emit(int(STKMC_val));
  if (size) { size->emit1(); delete size; }
}

struct CONSTNODE : public NODE {
  CONSTNODE(int V)               { value = V; defined = true; }
  virtual void emit1(void);      // load constant value onto stack
  virtual void emit2(void)       {;}
  virtual void link(AST next)    {;}
};

void CONSTNODE::emit1(void)
// load constant value onto stack
{ CGen->emit(int(STKMC_lit)); CGen->emit(value); }

struct PARAMNODE : public NODE {
  AST par, next;
  PARAMNODE(AST P)               { par = P; next = NULL; }
  virtual void emit1(void);      // load actual parameter onto stack
  virtual void emit2(void)       {;}
  virtual void link(AST param)   { next = param; }
};

void PARAMNODE::emit1(void)
// load actual parameter onto stack
{ if (par) { par->emit1(); delete par; }
  if (next) { next->emit1(); delete next; }  // follow link to next parameter
}

struct PROCNODE : public NODE {
  int level, entrypoint;     // static level and first instruction
  AST firstparam, lastparam; // pointers to argument list
  PROCNODE(int L, int ent)
    { level = L; entrypoint = ent; firstparam = NULL; lastparam = NULL; }
  virtual void emit1(void);      // generate procedure/function call
  virtual void emit2(void);      // generate process call
  virtual void link(AST next);   // link next actual parameter
};

void PROCNODE::emit1(void)
// generate procedure/function call
{ CGen->emit(int(STKMC_mst));
  if (firstparam) { firstparam->emit1(); delete firstparam; }
  CGen->emit(int(STKMC_cal));
  CGen->emit(level);
  CGen->emit(entrypoint);
}

void PROCNODE::emit2(void)
// generate process call
{ CGen->emit(int(STKMC_mst));
  if (firstparam) { firstparam->emit1(); delete firstparam; }
  CGen->emit(int(STKMC_frk));
  CGen->emit(entrypoint);
}

void PROCNODE::link(AST param)
// link next actual parameter
{ if (!firstparam) firstparam = param; else lastparam->link(param);
  lastparam = param;
}

// +++++++++++++++ code generator constructor +++++++++++++++++++++++++

CGEN::CGEN(REPORT *R)
{ undefined = 0;    // for forward references (exported)
  Report = R;
  generatingcode = true;
  codetop = 0;
  stktop = STKMC_memsize - 1;
}

void CGEN::emit(int word)
// Code generator for single word
{ if (!generatingcode) return;
  if (codetop >= stktop) { Report->error(212); generatingcode = false; }
  else { Machine->mem[codetop] = word; codetop++; }
}

bool CGEN::isrefast(AST a)
{ return a && a->refnode; }

// +++++++++++++++ routines that build the tree +++++++++++++++++++++++++

AST CGEN::emptyast(void)
{ return NULL; }

void CGEN::negateinteger(AST &i)
{ if (i && i->defined) { i->value = -i->value; return; }  // simple folding
  i = new MONOPNODE(CGEN_opsub, i);
}

void CGEN::binaryop(CGEN_operators op, AST &left, AST &right)
{ if (left && right && left->defined && right->defined)
  { // simple constant folding - better range checking needed
    switch (op)
    { case CGEN_opadd: left->value += right->value; break;
      case CGEN_opsub: left->value -= right->value; break;
      case CGEN_opmul: left->value *= right->value; break;
      case CGEN_opdvd:
        if (right->value == 0) Report->error(224);
        else left->value /= right->value;
        break;
      case CGEN_oplss: left->value = (left->value < right->value); break;
      case CGEN_opgtr: left->value = (left->value > right->value); break;
      case CGEN_opleq: left->value = (left->value <= right->value); break;
      case CGEN_opgeq: left->value = (left->value >= right->value); break;
      case CGEN_opeql: left->value = (left->value == right->value); break;
      case CGEN_opneq: left->value = (left->value != right->value); break;
    }
    delete right;
    return;
  }
  left = new BINOPNODE(op, left, right);
}

void CGEN::binaryintegerop(CGEN_operators op, AST &l, AST &r)
{ binaryop(op, l, r); }

void CGEN::comparison(CGEN_operators op, AST &l, AST &r)
{ binaryop(op, l, r); }

void CGEN::stackconstant(AST &c, int number)
{ c = new CONSTNODE(number); }

void CGEN::stackaddress(AST &v, int level, int offset, bool byref)
{ v = new VARNODE(byref, level, offset); }

void CGEN::linkparameter(AST &p, AST &par)
{ AST param = new PARAMNODE(par); p->link(param); }

void CGEN::stackreference(AST &base, bool byref, int level, int offset,
                          AST &size)
{ if (base) delete base; base = new REFNODE(byref, level, offset, size); }

void CGEN::subscript(AST &base, bool byref, int level, int offset,
                     AST &size, AST &index)
// Note the folding of constant indexing of arrays, and compile time
// range checking
{ if (!index || !index->defined || !size || !size->defined)
  { if (base) delete base;
    base = new INDEXNODE(byref, level, offset, size, index);
    return;
  }
  if (unsigned(index->value) >= size->value) // range check immediately
    Report->error(223);
  else
  { if (base) delete base;
    base = new VARNODE(byref, level, offset + index->value);
  }
  delete index; delete size;
}

void CGEN::markstack(AST &p, int level, int entrypoint)
{ p = new PROCNODE(level, entrypoint); }

// +++++++++++++++ code generation requiring tree walk ++++++++++++++++++

void CGEN::jumponfalse(AST condition, CGEN_labels &here,
                       CGEN_labels destination)
{ if (condition) { condition->emit1(); delete condition; }
  here = codetop; emit(int(STKMC_bze)); emit(destination);
}

void CGEN::assign(AST dest, AST expr)
{ if (dest) { dest->emit2(); delete dest; }
  if (expr) { expr->emit1(); delete expr; emit(int(STKMC_sto)); }
}

void CGEN::readvalue(AST i)
{ if (i) { i->emit2(); delete i; } emit(int(STKMC_inn)); }

void CGEN::writevalue(AST i)
{ if (i) { i->emit1(); delete i; } emit(int(STKMC_prn)); }

void CGEN::call(AST &p)
{ if (p) { p->emit1(); delete p; } }

void CGEN::signalop(AST s)
{ if (s) { s->emit2(); delete s; } emit(int(STKMC_sig)); }

void CGEN::waitop(AST s)
{ if (s) { s->emit2(); delete s; } emit(int(STKMC_wgt)); }

void CGEN::forkprocess(AST &p)
{ if (p) { p->emit2(); delete p; } }

// +++++++++++++++ code generation not requiring tree walk ++++++++++++++

void CGEN::newline(void)
{ emit(int(STKMC_nln)); }

void CGEN::writestring(CGEN_labels location)
{ emit(int(STKMC_prs)); emit(location); }

void CGEN::stackstring(char *str, CGEN_labels &location)
{ int l = strlen(str);
  if (stktop <= codetop + l + 1)
    { Report->error(212); generatingcode = false; return; }
  location = stktop - 1;
  for (int i = 0; i < l; i++) { stktop--; Machine->mem[stktop] = str[i]; }
  stktop--; Machine->mem[stktop] = 0;
}

void CGEN::dereference(AST &a)
{ /* not needed */ }

void CGEN::openstackframe(int size)
{ if (size > 0) { emit(int(STKMC_dsp)); emit(size); } }

void CGEN::leaveprogram(void)
{ emit(int(STKMC_hlt)); }

void CGEN::leavefunction(int blocklevel)
{ emit(int(STKMC_ret)); emit(blocklevel); emit(1); }

void CGEN::functioncheck(void)
{ emit(int(STKMC_nfn)); }

void CGEN::leaveprocedure(int blocklevel)
{ emit(int(STKMC_ret)); emit(blocklevel); emit(0); }

void CGEN::cobegin(CGEN_labels &location)
{ location = codetop; emit(int(STKMC_cbg)); emit(undefined); }

void CGEN::coend(CGEN_labels location, int number)
{ if (number >= STKMC_procmax) Report->error(216);
  else { Machine->mem[location+1] = number; emit(int(STKMC_cnd)); }
}

void CGEN::storelabel(CGEN_labels &location)
{ location = codetop; }

void CGEN::jump(CGEN_labels &here, CGEN_labels destination)
{ here = codetop; emit(int(STKMC_brn)); emit(destination); }

void CGEN::backpatch(CGEN_labels location)
{ if (codetop == location + 2 && STKMC_opcodes(Machine->mem[location]) == STKMC_brn)
    codetop -= 2;
  else
    Machine->mem[location+1] = codetop;
}

void CGEN::dump(void)
{ emit(int(STKMC_stk)); }

void CGEN::getsize(int &codelength, int &initsp)
{ codelength = codetop; initsp = stktop; }

int CGEN::gettop(void)
{ return codetop; }
